/*
 * Copyright: 2018 smarabbit studio.
 *
 * Licensed under the Confluent Community License; you may not use this file
 * except in compliance with the License.  You may obtain a copy of the License at
 *
 * http://www.confluent.io/confluent-community-license
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * @作   者： 黄开晖 (117227773@qq.com)
 * @日   期:  2019年2月24日
 */
package com.massyframework.beanskin.runtime.service.metadata;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.commons.codec.digest.DigestUtils;

import com.massyframework.beanskin.service.metadata.ClassMetadata;
import com.massyframework.beanskin.service.metadata.FieldMetadata;
import com.massyframework.beanskin.service.metadata.MethodMetadata;

/**
 * 实现{@link ClassMetadata}的缺省类
 */
final class DefaultClassMetadata extends MetadataBase<Class<?>> implements ClassMetadata {
	
	private List<FieldMetadata> fields = new ArrayList<FieldMetadata>();
	private List<MethodMetadata> methods = new ArrayList<MethodMetadata>();
	
	private String digest;
		
	/**
	 * 构造方法
	 * @param original
	 */
	public DefaultClassMetadata(Class<?> original) {
		super(original);
		
		Field[] fields = original.getDeclaredFields();
		for (Field field: fields) {
			this.fields.add(new DefaultFieldMetadata(field));
		}
		this.fields = Collections.unmodifiableList(this.fields);
		
		Method[] methods = original.getDeclaredMethods();
		for (Method method: methods) {
			this.methods.add(new DefaultMethodMetadata(method));
		}
		this.methods = Collections.unmodifiableList(this.methods);
	}
	
	/* (non-Javadoc)
	 * @see com.massyframework.metadata.ClassMetadata#containsField(java.lang.String)
	 */
	@Override
	public boolean containsField(String fieldName) {
		Optional<FieldMetadata> optional
			=this.fields.stream()
				.filter(value -> value.getName().equals(fieldName))
				.findFirst();
		return optional.isPresent();
	}
	
	/* (non-Javadoc)
	 * @see com.massyframework.metadata.ClassMetadata#findFieldMetadata(java.lang.String)
	 */
	@Override
	public FieldMetadata findFieldMetadata(String fieldName) {
		Optional<FieldMetadata> optional 
			=this.fields.stream()
				.filter(value -> value.getName().equals(fieldName))
				.findFirst();
		return optional.orElse(null);
	}
	
	@Override
	public FieldMetadata findFieldMetadata(Predicate<FieldMetadata> filter) {
		if (filter == null) return null;
		
		Optional<FieldMetadata> optional =
				this.fields.stream()
					.filter(filter)
					.findFirst();
		return optional.orElse(null);
	}

	/* (non-Javadoc)
	 * @see com.massyframework.metadata.ClassMetadata#findMethodMetadata(java.lang.String, java.lang.Class[])
	 */
	@Override
	public MethodMetadata findMethodMetadata(String methodName, Class<?>[] paramTypes) {
		Optional<MethodMetadata> optional =
				this.methods.stream()
					.filter(value -> value.matched(methodName, paramTypes))
					.findFirst();
		return optional.orElse(null);
	}
	
	

	@Override
	public MethodMetadata findMethodMetadata(Predicate<MethodMetadata> filter) {
		if (filter == null) return null;
		Optional<MethodMetadata> optional =
				this.methods.stream()
					.filter(filter)
					.findFirst();
		return optional.orElse(null);
	}

	/* (non-Javadoc)
	 * @see com.massyframework.metadata.Metadata#findAnnotation(java.lang.Class)
	 */
	@Override
	public <A extends Annotation> A findAnnotation(Class<A> annoType) {
		return this.getOriginal().getDeclaredAnnotation(annoType);
	}

	/* (non-Javadoc)
	 * @see com.massyframework.metadata.Metadata#findAnnotations(java.lang.Class)
	 */
	@Override
	public <A extends Annotation> A[] findAnnotations(Class<A> annoType) {
		return this.getOriginal().getDeclaredAnnotationsByType(annoType);
	}
	
	/* (non-Javadoc)
	 * @see org.camellia.model.metadata.ClassMetadata#getDigest()
	 */
	@Override
	public synchronized String getDigest() {
		if (this.digest == null) {
			this.digest = DigestUtils.md5Hex(this.getOriginal().getName());
		}
		return this.digest;
	}

	/* (non-Javadoc)
	 * @see com.massyframework.metadata.ClassMetadata#getFieldNames()
	 */
	@Override
	public List<String> getFieldNames() {
		return	this.fields.stream()
					.map(value -> value.getName())
					.collect(Collectors.toList());
	}
	
	@Override
	public List<FieldMetadata> getFieldMetadatas(Predicate<FieldMetadata> filter) {
		if (filter == null) return Collections.emptyList();
		
		return this.fields.stream()
				.filter(filter)
				.collect(Collectors.toList());
	}
	
	/* (non-Javadoc)
	 * @see com.massyframework.metadata.ClassMetadata#findFieldMetadataWithAnnotation(java.lang.Class)
	 */
	@Override
	public <A extends Annotation> List<FieldMetadata> getFieldMetadatasWithAnnotation(Class<A> annoType) {
		List<FieldMetadata> result =
				this.fields.stream()
					.filter(value -> value.findAnnotation(annoType) != null)
					.collect(Collectors.toList());
				
		return result;
	}

	/* (non-Javadoc)
	 * @see com.massyframework.metadata.ClassMetadata#getMethodNames()
	 */
	@Override
	public List<String> getMethodNames() {
		Set<String> set = this.methods.stream()
							.map(value -> value.getName())
							.collect(Collectors.toSet());
		return new ArrayList<String>(set);
	}

	/* (non-Javadoc)
	 * @see com.massyframework.metadata.ClassMetadata#getMethodMetadatas()
	 */
	@Override
	public List<MethodMetadata> getMethodMetadatas() {
		return this.methods;
	}
	
	
	@Override
	public <A extends Annotation> List<MethodMetadata> getMethodMetadatasWithAnnotation(Class<A> annoType) {
		List<MethodMetadata> result =
				this.methods.stream()
					.filter(value -> value.findAnnotation(annoType) != null)
					.collect(Collectors.toList());
		return result;
	}

	/* (non-Javadoc)
	 * @see com.massyframework.metadata.ClassMetadata#getMethodMetadatas(java.lang.String)
	 */
	@Override
	public List<MethodMetadata> getMethodMetadatas(String methodName) {
		return this.methods.stream()
					.filter(value -> value.getName().equals(methodName))
					.collect(Collectors.toList());
	}
	
	
	
	/* (non-Javadoc)
	 * @see com.massyframework.metadata.Metadata#getName()
	 */
	@Override
	public String getName() {
		return this.getOriginal().getName();
	}
		
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "DefaultClassMetadata [className=" + this.getName() + "]";
	}
}
